home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / IFC_112 / netscape / util / Archiver.java < prev    next >
Encoding:
Text File  |  1999-05-28  |  14.5 KB  |  445 lines  |  [TEXT/CWIE]

  1. // Archiver.java
  2. // By Ned Etcode
  3. // Copyright 1995, 1996, 1997 Netscape Communications Corp.  All rights reserved.
  4.  
  5. package netscape.util;
  6.  
  7. import java.io.*;
  8.  
  9. /** Object subclass implementing the Encoder interface to encode a graph of
  10.   * objects to an Archive. The following example demonstrates how to use an
  11.   * Archiver to write a graph of objects, starting from rootObject to the
  12.   * stream System.out:
  13.   *
  14.   * <pre>
  15.   *    archiver = new Archiver(new Archive());
  16.   *    archiver.archiveRootObject(rootObject);
  17.   *    archiver.archive().writeASCII(System.out, true);
  18.   * </pre>
  19.   *
  20.   * @see Encoder
  21.   * @see Archive
  22.   */
  23. public class Archiver implements Encoder {
  24.     Archive archive;
  25.     ArchivingStack stack = new ArchivingStack();
  26.     IdHashtable idHash = new IdHashtable(false);
  27.  
  28.     Object currentObject;
  29.     ClassTable currentTable;
  30.     int currentId;
  31.     int currentColumnCount;
  32.     int currentRow;
  33.     int currentColumn;
  34.  
  35.     /** Constructs an Archiver that writes to <b>archive</b>.
  36.       */
  37.     public Archiver(Archive archive) {
  38.         super();
  39.         this.archive = archive;
  40.     }
  41.  
  42.     /** Returns the archive used by the Archiver.
  43.       */
  44.     public Archive archive() {
  45.         return archive;
  46.     }
  47.  
  48.     /** A convenience method for writing an object to a stream.  Equivilent to:
  49.       * <pre>
  50.       *    archive = new Archive();
  51.       *    archiver = new Archiver(archive);
  52.       *    archiver.archiveRootObject(root);
  53.       *    archive.write(out);
  54.       * </pre>
  55.       */
  56.     public static void writeObject(OutputStream outputStream, Object root)
  57.         throws IOException, CodingException {
  58.         Archive archive;
  59.         Archiver archiver;
  60.  
  61.         archive = new Archive();
  62.         archiver = new Archiver(archive);
  63.         archiver.archiveRootObject(root);
  64.         archive.write(outputStream);
  65.     }
  66.  
  67.     /** Starts the archiving process. This method can be called multiple
  68.       * times to encode more than one graph (which may or may not overlap)
  69.       * into an Archive. This automatically adds the object to the Archive's
  70.       * array of root identifiers.
  71.       */
  72.     public void archiveRootObject(Object root) throws CodingException {
  73.         int rootId;
  74.  
  75.         // This will cause the object to be archived if we haven't seen it yet.
  76.  
  77.         rootId = identifierForObject(root);
  78.         archive.addRootIdentifier(rootId);
  79.     }
  80.  
  81.     /** Checks whether the archiver has seen this object before.
  82.       * If not, it gets a new identifier for the object, pushes the current
  83.       * archiving state, and archives the new object.
  84.       */
  85.     private int identifierForObject(Object object) throws CodingException {
  86.         int id;
  87.         ClassTable table;
  88.         ClassInfo info;
  89.         ExternalCoder coder;
  90.         String className;
  91.  
  92.         if (object == null)
  93.             return 0;
  94.  
  95.         id = idHash.get(object);
  96.         if (id != IdHashtable.NOT_FOUND)
  97.             return id;
  98.  
  99.         // If we have never seen this object before, then we need to create
  100.         // an identifier for it by inserting a row in the appropriate class
  101.         // table.
  102.  
  103.         className = object.getClass().getName();
  104.         coder = archive.externalCoderForName(className);
  105.         table = archive.classTableForName(className);
  106.  
  107.         if (table == null) {
  108.             info = new ClassInfo(className);
  109.  
  110.             if (coder != null)
  111.                 coder.describeClassInfo(object, info);
  112.             else
  113.                 ((Codable)object).describeClassInfo(info);
  114.  
  115.             table = new ClassTable(archive, info);
  116.             table.setUniqueStrings(true);
  117.             archive.addClassTable(table);
  118.         }
  119.  
  120.         id = table.newIdentifier();
  121.         idHash.putKnownAbsent(object, id);
  122.  
  123.         // We archive depth first, so set things up and archive this puppy.
  124.  
  125.         pushArchivingState(object, id);
  126.  
  127.         if (coder != null)
  128.             coder.encode(object, this);
  129.         else
  130.             ((Codable)object).encode(this);
  131.  
  132.         popArchivingState();
  133.  
  134.         return id;
  135.     }
  136.  
  137.     /** This method pushes any current archiving state onto the stack and sets
  138.       * up to begin archiving the given object. The pushed state will be
  139.       * restored in popArchivingState().
  140.       */
  141.     private void pushArchivingState(Object object, int id) {
  142.         stack.pushArchiver(this);
  143.  
  144.         currentObject = object;
  145.         currentTable = archive.classTableForIdentifier(id);
  146.         currentId = id;
  147.         currentRow = archive.rowForIdentifier(id);
  148.         currentColumn = -1;
  149.         currentColumnCount = currentTable.fieldCount;
  150.     }
  151.  
  152.     /** This method pops the stack of archiving states. The current object is
  153.       * picked up where we left off.
  154.       */
  155.     private void popArchivingState() {
  156.         stack.popArchiver(this);
  157.     }
  158.  
  159.     /** This method is called at the beginning of each archive... method
  160.       * to make sure that currentColumn matches the given key. In general,
  161.       * the keys will be in the same order as the columns so the pointer
  162.       * equality test will succeed and we'll rip right along.
  163.       */
  164.     private void prepareToArchiveField(String key) throws CodingException {
  165.         int i, count;
  166.         String fieldNames[];
  167.  
  168.         count = currentColumnCount;
  169.         fieldNames = currentTable.fieldNames;
  170.  
  171.         // Scan forward looking for a matching column.  It is kind of common
  172.         // to omit fields when archiving/unarchiving, so skipping forward a
  173.         // few after a miss will usually get us back on track.
  174.  
  175.         for (i = currentColumn + 1; i < count; i++) {
  176.             if (key == fieldNames[i]) {
  177.                 currentColumn = i;
  178.                 return;
  179.             }
  180.         }
  181.  
  182.         // Our optimism has not paid off.  Go ask the ClassTable for
  183.         // the column.
  184.  
  185.         currentColumn = currentTable.columnForField(key);
  186.         if (currentColumn < 0) {
  187.             throw new CodingException("Unknown field name: " + key);
  188.         }
  189.     }
  190.  
  191.     /** Encoder interface method that encodes the boolean <b>value</b>,
  192.       * associating it with the string <b>key</b>.
  193.       */
  194.     public void encodeBoolean(String key, boolean value)
  195.         throws CodingException {
  196.  
  197.         prepareToArchiveField(key);
  198.         currentTable.setBooleanAt(currentRow, currentColumn, value);
  199.     }
  200.  
  201.     /** Encoder interface method that encodes the boolean array <b>value</b>,
  202.       * associating it with the string <b>key</b>.
  203.       */
  204.     public void encodeBooleanArray(String key, boolean value[], int offset,
  205.         int length) throws CodingException {
  206.         boolean copy[] = null;
  207.  
  208.         prepareToArchiveField(key);
  209.         if (value != null) {
  210.             copy = new boolean[length];
  211.             System.arraycopy(value, offset, copy, 0, length);
  212.         }
  213.  
  214.         currentTable.setBooleanArrayAt(currentRow, currentColumn, copy);
  215.     }
  216.  
  217.     /** Encoder interface method that encodes the character <b>value</b>,
  218.       * associating it with the string <b>key</b>.
  219.       */
  220.     public void encodeChar(String key, char value) throws CodingException {
  221.         prepareToArchiveField(key);
  222.         currentTable.setCharAt(currentRow, currentColumn, value);
  223.     }
  224.  
  225.     /** Encoder interface method that encodes the character array <b>value</b>,
  226.       * associating it with the string <b>key</b>.
  227.       */
  228.     public void encodeCharArray(String key, char value[], int offset,
  229.         int length) throws CodingException {
  230.         char copy[] = null;
  231.  
  232.         prepareToArchiveField(key);
  233.         if (value != null) {
  234.             copy = new char[length];
  235.             System.arraycopy(value, offset, copy, 0, length);
  236.         }
  237.  
  238.         currentTable.setCharArrayAt(currentRow, currentColumn, copy);
  239.     }
  240.  
  241.     /** Encoder interface method that encodes the byte <b>value</b>,
  242.       * associating it with the string <b>key</b>.
  243.       */
  244.     public void encodeByte(String key, byte value) throws CodingException {
  245.         prepareToArchiveField(key);
  246.         currentTable.setByteAt(currentRow, currentColumn, value);
  247.     }
  248.  
  249.     /** Encoder interface method that encodes the byte array <b>value</b>,
  250.       * associating it with the string <b>key</b>.
  251.       */
  252.     public void encodeByteArray(String key, byte value[], int offset,
  253.         int length) throws CodingException {
  254.         byte copy[] = null;
  255.  
  256.         prepareToArchiveField(key);
  257.         if (value != null) {
  258.             copy = new byte[length];
  259.             System.arraycopy(value, offset, copy, 0, length);
  260.         }
  261.  
  262.         currentTable.setByteArrayAt(currentRow, currentColumn, copy);
  263.     }
  264.  
  265.     /** Encoder interface method that encodes the short <b>value</b>,
  266.       * associating it with the string <b>key</b>.
  267.       */
  268.     public void encodeShort(String key, short value) throws CodingException {
  269.         prepareToArchiveField(key);
  270.         currentTable.setShortAt(currentRow, currentColumn, value);
  271.     }
  272.  
  273.     /** Encoder interface method that encodes the short array <b>value</b>,
  274.       * associating it with the string <b>key</b>.
  275.       */
  276.     public void encodeShortArray(String key, short value[], int offset,
  277.         int length) throws CodingException {
  278.         short copy[] = null;
  279.  
  280.         prepareToArchiveField(key);
  281.         if (value != null) {
  282.             copy = new short[length];
  283.             System.arraycopy(value, offset, copy, 0, length);
  284.         }
  285.  
  286.         currentTable.setShortArrayAt(currentRow, currentColumn, copy);
  287.     }
  288.  
  289.     /** Encoder interface method that encodes the integer <b>value</b>,
  290.       * associating it with the string <b>key</b>.
  291.       */
  292.     public void encodeInt(String key, int value) throws CodingException {
  293.         prepareToArchiveField(key);
  294.         currentTable.setIntAt(currentRow, currentColumn, value);
  295.     }
  296.  
  297.     /** Encoder interface method that encodes the integer array <b>value</b>,
  298.       * associating it with the string <b>key</b>.
  299.       */
  300.     public void encodeIntArray(String key, int value[], int offset,
  301.         int length) throws CodingException {
  302.         int copy[] = null;
  303.  
  304.         prepareToArchiveField(key);
  305.         if (value != null) {
  306.             copy = new int[length];
  307.             System.arraycopy(value, offset, copy, 0, length);
  308.         }
  309.  
  310.         currentTable.setIntArrayAt(currentRow, currentColumn, copy);
  311.     }
  312.  
  313.     /** Encoder interface method that encodes the long <b>value</b>,
  314.       * associating it with the string <b>key</b>.
  315.       */
  316.     public void encodeLong(String key, long value) throws CodingException {
  317.         prepareToArchiveField(key);
  318.         currentTable.setLongAt(currentRow, currentColumn, value);
  319.     }
  320.  
  321.     /** Encoder interface method that encodes the long array <b>value</b>,
  322.       * associating it with the string <b>key</b>.
  323.       */
  324.     public void encodeLongArray(String key, long value[], int offset,
  325.         int length) throws CodingException {
  326.         long copy[] = null;
  327.  
  328.         prepareToArchiveField(key);
  329.         if (value != null) {
  330.             copy = new long[length];
  331.             System.arraycopy(value, offset, copy, 0, length);
  332.         }
  333.  
  334.         currentTable.setLongArrayAt(currentRow, currentColumn, copy);
  335.     }
  336.  
  337.     /** Encoder interface method that encodes the float <b>value</b>,
  338.       * associating it with the string <b>key</b>.
  339.       */
  340.     public void encodeFloat(String key, float value) throws CodingException {
  341.         prepareToArchiveField(key);
  342.         currentTable.setFloatAt(currentRow, currentColumn, value);
  343.     }
  344.  
  345.     /** Encoder interface method that encodes the float array <b>value</b>,
  346.       * associating it with the string <b>key</b>.
  347.       */
  348.     public void encodeFloatArray(String key, float value[], int offset,
  349.         int length) throws CodingException {
  350.         float copy[] = null;
  351.  
  352.         prepareToArchiveField(key);
  353.         if (value != null) {
  354.             copy = new float[length];
  355.             System.arraycopy(value, offset, copy, 0, length);
  356.         }
  357.  
  358.         currentTable.setFloatArrayAt(currentRow, currentColumn, copy);
  359.     }
  360.  
  361.     /** Encoder interface method that encodes the double <b>value</b>,
  362.       * associating it with the string <b>key</b>.
  363.       */
  364.     public void encodeDouble(String key, double value) throws CodingException {
  365.         prepareToArchiveField(key);
  366.         currentTable.setDoubleAt(currentRow, currentColumn, value);
  367.     }
  368.  
  369.     /** Encoder interface method that encodes the double array <b>value</b>,
  370.       * associating it with the string <b>key</b>.
  371.       */
  372.     public void encodeDoubleArray(String key, double value[], int offset,
  373.         int length) throws CodingException {
  374.         double copy[] = null;
  375.  
  376.         prepareToArchiveField(key);
  377.         if (value != null) {
  378.             copy = new double[length];
  379.             System.arraycopy(value, offset, copy, 0, length);
  380.         }
  381.  
  382.         currentTable.setDoubleArrayAt(currentRow, currentColumn, copy);
  383.     }
  384.  
  385.     /** Encoder interface method that encodes the string <b>value</b>,
  386.       * associating it with the string <b>key</b>.
  387.       */
  388.     public void encodeString(String key, String value) throws CodingException {
  389.         prepareToArchiveField(key);
  390.         currentTable.setStringAt(currentRow, currentColumn, value);
  391.     }
  392.  
  393.     /** Encoder interface method that encodes the string array <b>value</b>,
  394.       * associating it with the string <b>key</b>.
  395.       */
  396.     public void encodeStringArray(String key, String value[], int offset,
  397.         int length) throws CodingException {
  398.         String copy[] = null;
  399.  
  400.         prepareToArchiveField(key);
  401.         if (value != null) {
  402.             copy = new String[length];
  403.             System.arraycopy(value, offset, copy, 0, length);
  404.         }
  405.  
  406.         currentTable.setStringArrayAt(currentRow, currentColumn, copy);
  407.     }
  408.  
  409.     /** Encoder interface method that encodes a reference to another Codable
  410.       * object. If multiple objects reference the same object and each passes
  411.       * it to <b>encodeObject()</b>, only one copy of that object is actually
  412.       * encoded.
  413.       */
  414.     public void encodeObject(String key, Object value)
  415.         throws CodingException {
  416.         prepareToArchiveField(key);
  417.         currentTable.setIdentifierAt(currentRow, currentColumn,
  418.             identifierForObject(value));
  419.     }
  420.  
  421.     /** Encoder interface method that encodes an array of Codable objects. The
  422.       * reference to the array is not shared, but references to the objects in
  423.       * the array are.
  424.       */
  425.     public void encodeObjectArray(String key, Object value[], int offset,
  426.         int length) throws CodingException {
  427.         int i, ids[];
  428.  
  429.         prepareToArchiveField(key);
  430.  
  431.         if (value == null) {
  432.             currentTable.setIdentifierArrayAt(currentRow, currentColumn, null);
  433.             return;
  434.         }
  435.  
  436.         ids = new int[length];
  437.  
  438.         for (i = offset; i < length; i++) {
  439.             ids[i] = identifierForObject(value[i]);
  440.         }
  441.  
  442.         currentTable.setIdentifierArrayAt(currentRow, currentColumn, ids);
  443.     }
  444. }
  445.